#include "ccms/nr/mem_stats.h"
#include "arlib/raii/file_handle.h"
#include "arlib/exception/invalid_argument_exception.h"
#include "arlib/exception/errno_exception.h"
#include <cstring>
#include <cerrno>

#define THROW_INVALIDARGUMENTEXCEPTION(reason) throw arlib::exception::InvalidArgumentException(__FILE__, __LINE__, reason)
#define THROW_ERRNOEXCEPTION(reason, errnum) throw arlib::exception::ErrnoException(__FILE__, __LINE__, reason, errnum)

#define BUFFER_SIZE 128

using namespace ccms::nr;

namespace ccms {
namespace nr {

MemStats::MemStats(void)
{
    memset(&memUsage, '\0', sizeof(MemUsage));
}

void MemStats::update(void)
{
    arlib::raii::FileHandle fh;
    char buffer[BUFFER_SIZE];

    fh.reset(fopen("/proc/meminfo", "rb"));
    if(!fh.get()) {
        THROW_ERRNOEXCEPTION("Unable to open /proc/meminfo", errno);
    }

    do {
        memset(buffer, '\0', sizeof(buffer));
        if(!fgets(buffer, BUFFER_SIZE, fh.get())) {
            if(feof(fh.get())) {
                break;
            } else {    
                THROW_ERRNOEXCEPTION("Could not read from /proc/meminfo", ferror(fh.get()));
            }
        }

        if(memUsage.physTotal == 0) {
            if(sscanf(buffer, "MemTotal: %lu", &memUsage.physTotal) == 1)
                continue;
        }

        if(memUsage.swapTotal == 0) {
            if(sscanf(buffer, "SwapTotal: %lu", &memUsage.swapTotal) == 1)
                continue;
        }

        if(sscanf(buffer, "MemFree: %lu", &memUsage.physFree) == 1)
            continue;
        if(sscanf(buffer, "Buffers: %lu", &memUsage.physBuffers) == 1)
            continue;
        if(sscanf(buffer, "Cached: %lu", &memUsage.physCached) == 1)
            continue;
        if(sscanf(buffer, "SwapFree: %lu", &memUsage.swapFree) == 1)
            continue;
    } while(!feof(fh.get()));

    memUsage.physUsed = memUsage.physTotal - memUsage.physFree
        - memUsage.physBuffers - memUsage.physCached;

    memUsage.swapUsed = memUsage.swapTotal - memUsage.swapFree;
}

unsigned long MemStats::getMemUsage(const MemUsageMode mode) const
{
    unsigned long ret;

    switch(mode) {
        case MEM_USAGE_MODE_PHYS_TOTAL:
            ret = memUsage.physTotal;
            break;
        case MEM_USAGE_MODE_PHYS_FREE:
            ret = memUsage.physFree;
            break;
        case MEM_USAGE_MODE_PHYS_BUFFERS:
            ret = memUsage.physBuffers;
            break;
        case MEM_USAGE_MODE_PHYS_CACHED:
            ret = memUsage.physCached;
            break;
        case MEM_USAGE_MODE_PHYS_USED:
            ret = memUsage.physUsed;
            break;
        case MEM_USAGE_MODE_SWAP_TOTAL:
            ret = memUsage.swapTotal;
            break;
        case MEM_USAGE_MODE_SWAP_FREE:
            ret = memUsage.swapFree;
            break;
        case MEM_USAGE_MODE_SWAP_USED:
            ret = memUsage.swapUsed;
            break;
        default:
            THROW_INVALIDARGUMENTEXCEPTION("Invalid mode");
    }

    return ret;
}

} // namespace nr
} // namespace ccms
